#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <clamav.h>

#include <sys/time.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <stdint.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>


#include "sigTree.h"

#define MICRO_SECONDS 1000000
#define BUFFER_SIZE 1000


using namespace sigMatch;

namespace {

FILE* fp;

int fp1;
struct sigRoot* root;
char* buff;
long int tbuff;
int len;
long int buffsize;

/*
 * 计算所用时间
 */

long timeDiff(struct timeval newerTime, struct timeval olderTime)
{
  long long diffs;
  diffs = (newerTime.tv_sec-olderTime.tv_sec)*MICRO_SECONDS+
  newerTime.tv_usec-olderTime.tv_usec;
  diffs /= 1000;
  return diffs;
}

}


//Clmav初始化
struct cl_engine * initClamav(){
       int ret=0;
      unsigned int sigs=0;
      struct cl_engine *engine;
      if((ret = cl_init(CL_INIT_DEFAULT)) != CL_SUCCESS) {
	      printf("Can't initialize libclamav: %s\n", cl_strerror(ret));
          return NULL;
       }

      if(!(engine = cl_engine_new())) {
            printf("Can't create new engine\n");
            return NULL;
       }

      if((ret = cl_load(cl_retdbdir(), engine, &sigs, CL_DB_STDOPT)) != CL_SUCCESS) {
           printf("cl_load: %s\n", cl_strerror(ret));

           cl_engine_free(engine);
           return NULL;
       }

       if((ret = cl_engine_compile(engine)) != CL_SUCCESS) {
           printf("Database initialization error: %s\n", cl_strerror(ret));;
           cl_engine_free(engine);

           return NULL;
        }
        return engine;              //clamav初始化成功
}

//clamav默认的扫描文件大小位25M
void setMaxScanSize(cl_engine *engine,int multiple){
         int r;

         //扫描大小
         uint64_t val64 = cl_engine_get_num(engine, CL_ENGINE_MAX_SCANSIZE, &r);
         val64 = val64 * multiple;
         cl_engine_set_num(engine, CL_ENGINE_MAX_SCANSIZE, val64);


         //文件大小：
         val64 = cl_engine_get_num(engine, CL_ENGINE_MAX_FILESIZE, &r);
         val64 = val64 * multiple;
         cl_engine_set_num(engine, CL_ENGINE_MAX_FILESIZE, val64);

         printf ("setClamavMaxFileSize is %dMB\n" , (int) val64/1048576);
}


int main(int argc, char **argv)
{
  int fd,ret=0;
  unsigned long int size = 0;
  long double scandata;
  const char**virname;

  len = 6;

  struct timeval clamTimeBefore, clamTimeAfter;
  double clammbps;

  struct timeval sigTimeBefore, sigTimeAfter;
  double sigmbps;

  char * sigfile="sig.txt";
  char *  sigdir="fastdir";

   //加载命令参数
  if(argc != 4) {
    printf("请输入参数: %s <file>  <bloom_size> <buffsize> \n", argv[0]);
    return 2;
  }

  if((fd = open(argv[1], O_RDONLY)) == -1) {
	  printf("不能打开文件 %s\n", argv[1]);
    return 2;
  }


  buffsize = atoi(argv[3]);
  struct cl_engine *engine=initClamav();
  if(NULL==engine){
       printf("clamav初始化失败！\n");
       return 2;
  }


  BLOOM_SIZE = atoi(argv[2]);



  setMaxScanSize(engine,1024);     //设置文件大小可为1G






/* Clamav扫描 */
 printf("clamav start scaning.......\n");
  gettimeofday(&clamTimeBefore, NULL);
  if((ret = cl_scandesc(fd, virname, &size, engine, CL_SCAN_STDOPT)) == CL_VIRUS) {
       printf("virus by clamav:%s;\n", *virname);
  } else {
    if(ret == CL_CLEAN) {
      printf("virus by clamav:%s;\n","novir");
	  } else {
      printf("Error: %s\n", cl_strerror(ret));
      cl_engine_free(engine);
      close(fd);
      return 2;
    }
  }

  gettimeofday(&clamTimeAfter, NULL);
  close(fd);
/*Clamav扫描结束*/


//sigscan初始化
  root = 	createTree(sigfile, len, sigdir);

  fp = fopen (argv[1], "r");
  long numbuff = 0, thrown = 0;

  buff = (char*) malloc (sizeof (char) *buffsize);

  unsigned long int ind1 = 0;

//sigscan开始扫描

printf("sigmatch start scaning.......\n");
 long clamTime = timeDiff(clamTimeAfter,clamTimeBefore);
gettimeofday(&sigTimeBefore, NULL);

int flag=0;
cl_fmap_t * buff1;

//判断文件大小
fseek(fp,0,SEEK_END);   //指针：移动到文件尾部
long n = ftell(fp);

if(n<buffsize)
     buffsize=n;



fseek(fp,0,SEEK_SET);
while (fread(buff, buffsize,1, fp)){
    numbuff++;
    if((ret = sigScanbuff(root,(unsigned char*) buff, buffsize, len/2)) == 0) {
                buff1=cl_fmap_open_memory(buff, buffsize);
               if((ret = cl_scanmap_callback(buff1,virname, &ind1, engine,CL_SCAN_STDOPT,NULL))== CL_VIRUS) {   //扫描buff用到的函数
                    printf("virus by sigmatch:%s;\n", *virname);
                   flag=1;
                    break;
               }
    }
    else {
      thrown ++;
    }

     if(ftell(fp)!=n)
        fseek(fp,-5,SEEK_CUR);
     else break;

    if((n-ftell(fp))<buffsize){
            buffsize=(n-ftell(fp));
       }

  }


if(flag==0){
       printf("virus by sigmatch:%s;\n","novir");
}


  gettimeofday(&sigTimeAfter, NULL);


  long sigTime = timeDiff(sigTimeAfter,sigTimeBefore);
  fclose(fp);

/*sigscan扫描结束*/

	/* 计算结果 */
  scandata = size * (CL_COUNT_PRECISION / 1024) / 1024.0;
  clammbps =(double) (scandata/(double) clamTime) * 1000 ;
  sigmbps =(double) (scandata/(double) sigTime) * 1000 ;

       //打印结果
  printf("——  ——  ——  —— scan result and contrast —— —— —— ——\n");
  printf("data scaned:%2.2Lf MB;\n", scandata);
  printf("clamav time：%ldms;               ", clamTime);
  printf("sigmatch time：%ldms;\n", sigTime);
  printf("clamav rate：%f MBps;            ",clammbps);
  printf("sigmatch rate：%f MBps;\n",sigmbps);
  printf("sigmatch filter : %f\n", ((double)thrown/(double)numbuff));
  printf("sigmatch vs clamav speedup：%f;\n", (double) sigmbps/clammbps);


  /* 释放空间 */
  free(buff);
  free(root);
  cl_engine_free(engine);
  return ret == CL_VIRUS ? 1 : 0;

}
